import asyncio
import subprocess
import sys
from typing import Dict, Any, Optional, Callable
from pathlib import Path
from loguru import logger
import tempfile
import os


class ScriptExecutor:
    """脚本执行器"""
    
    def __init__(self):
        self.process: Optional[subprocess.Popen] = None
        self.is_running = False
        self.output_callback: Optional[Callable[[str], None]] = None
        self.error_callback: Optional[Callable[[str], None]] = None
    
    def set_output_callback(self, callback: Callable[[str], None]) -> None:
        """设置输出回调函数"""
        self.output_callback = callback
    
    def set_error_callback(self, callback: Callable[[str], None]) -> None:
        """设置错误回调函数"""
        self.error_callback = callback
    
    async def execute_script(self, script_code: str, timeout: int = 60) -> Dict[str, Any]:
        """执行脚本代码"""
        try:
            self.is_running = True
            result = {
                "success": False,
                "output": "",
                "error": "",
                "duration": 0
            }
            
            # 创建临时脚本文件
            with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as f:
                f.write(script_code)
                temp_script_path = f.name
            
            try:
                # 执行脚本
                start_time = asyncio.get_event_loop().time()
                
                self.process = subprocess.Popen(
                    [sys.executable, temp_script_path],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    text=True,
                    bufsize=1,
                    universal_newlines=True
                )
                
                # 异步读取输出
                stdout_task = asyncio.create_task(self._read_stream(self.process.stdout, "stdout"))
                stderr_task = asyncio.create_task(self._read_stream(self.process.stderr, "stderr"))
                
                # 等待进程完成或超时
                try:
                    await asyncio.wait_for(
                        self._wait_for_process(), 
                        timeout=timeout
                    )
                except asyncio.TimeoutError:
                    logger.warning(f"脚本执行超时 ({timeout}秒), 终止进程")
                    self._terminate_process()
                    result["error"] = f"脚本执行超时 ({timeout}秒)"
                
                # 等待输出读取完成
                await asyncio.gather(stdout_task, stderr_task, return_exceptions=True)
                
                end_time = asyncio.get_event_loop().time()
                result["duration"] = end_time - start_time
                
                # 获取返回码
                return_code = self.process.returncode if self.process else -1
                result["success"] = return_code == 0
                
                if return_code != 0:
                    result["error"] = f"脚本执行失败，返回码: {return_code}"
                
                logger.info(f"脚本执行完成，返回码: {return_code}, 耗时: {result['duration']:.2f}秒")
                
            finally:
                # 清理临时文件
                try:
                    os.unlink(temp_script_path)
                except:
                    pass
            
            return result
            
        except Exception as e:
            logger.error(f"执行脚本失败: {e}")
            return {
                "success": False,
                "output": "",
                "error": str(e),
                "duration": 0
            }
        finally:
            self.is_running = False
            self.process = None
    
    async def _wait_for_process(self) -> None:
        """等待进程完成"""
        if not self.process:
            return
        
        while self.process.poll() is None:
            await asyncio.sleep(0.1)
    
    async def _read_stream(self, stream, stream_type: str) -> None:
        """异步读取流输出"""
        if not stream:
            return
        
        try:
            while True:
                line = stream.readline()
                if not line:
                    break
                
                line = line.strip()
                if line:
                    if stream_type == "stdout":
                        logger.info(f"[脚本输出] {line}")
                        if self.output_callback:
                            await asyncio.get_event_loop().run_in_executor(
                                None, self.output_callback, line
                            )
                    else:  # stderr
                        logger.error(f"[脚本错误] {line}")
                        if self.error_callback:
                            await asyncio.get_event_loop().run_in_executor(
                                None, self.error_callback, line
                            )
        except Exception as e:
            logger.error(f"读取流失败: {e}")
    
    def _terminate_process(self) -> None:
        """终止进程"""
        if self.process:
            try:
                self.process.terminate()
                # 给进程一些时间优雅关闭
                try:
                    self.process.wait(timeout=5)
                except subprocess.TimeoutExpired:
                    # 如果优雅关闭失败，强制杀死
                    self.process.kill()
                    self.process.wait()
            except:
                pass
    
    def stop_execution(self) -> None:
        """停止脚本执行"""
        if self.is_running and self.process:
            logger.info("用户请求停止脚本执行")
            self._terminate_process()
    
    async def execute_script_file(self, script_path: str, timeout: int = 60) -> Dict[str, Any]:
        """执行脚本文件"""
        try:
            if not Path(script_path).exists():
                raise FileNotFoundError(f"脚本文件不存在: {script_path}")
            
            with open(script_path, 'r', encoding='utf-8') as f:
                script_code = f.read()
            
            return await self.execute_script(script_code, timeout)
            
        except Exception as e:
            logger.error(f"执行脚本文件失败: {e}")
            return {
                "success": False,
                "output": "",
                "error": str(e),
                "duration": 0
            }


class ScriptValidator:
    """脚本验证器"""
    
    @staticmethod
    def validate_script_syntax(script_code: str) -> Dict[str, Any]:
        """验证脚本语法"""
        try:
            compile(script_code, '<string>', 'exec')
            return {"valid": True, "error": None}
        except SyntaxError as e:
            return {
                "valid": False,
                "error": f"语法错误: 行 {e.lineno}, {e.msg}"
            }
        except Exception as e:
            return {
                "valid": False,
                "error": f"验证失败: {str(e)}"
            }
    
    @staticmethod
    def check_dependencies(script_code: str) -> Dict[str, Any]:
        """检查脚本依赖"""
        import ast
        
        try:
            tree = ast.parse(script_code)
            imports = []
            
            for node in ast.walk(tree):
                if isinstance(node, ast.Import):
                    for alias in node.names:
                        imports.append(alias.name)
                elif isinstance(node, ast.ImportFrom):
                    if node.module:
                        imports.append(node.module)
            
            # 检查Playwright依赖
            has_playwright = any('playwright' in imp for imp in imports)
            
            return {
                "imports": imports,
                "has_playwright": has_playwright,
                "missing_dependencies": []  # 这里可以添加更复杂的依赖检查逻辑
            }
            
        except Exception as e:
            return {
                "imports": [],
                "has_playwright": False,
                "error": str(e)
            }